package com.atguigu.gmall.index.util;

import com.google.common.base.Charsets;
import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnels;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.Arrays;
import java.util.Timer;
import java.util.TimerTask;

@Component
public class DistributedLock {

    @Autowired
    private StringRedisTemplate redisTemplate;

    public Boolean tryLock(String lockName , String uuid ,Integer expire){
        /**
         * 如果查询lock为0，说明没人获取锁，或者根据lock和uuid查询到返回1，说明自己获取了锁
         * 则获取锁并且在锁的value上+1，并且给锁设置过期时间为expire，并且返回1
         * 如果没有获取到锁则返回0
         */
        String script = "if redis.call('exists',KEYS[1]) == 0 or redis.call('hexists',KEYS[1],ARGV[1]) == 1 " +
                "then " +
                "redis.call('hincrby',KEYS[1],ARGV[1],1) redis.call('expire',KEYS[1],ARGV[2]) " +
                "return 1 " +
                "else " +
                "return 0 " +
                "end";
        Boolean flag = redisTemplate.execute(new DefaultRedisScript<>(script, Boolean.class), Arrays.asList(lockName), uuid, expire.toString());
        if (!flag){
            //重试
            try {
                Thread.sleep(40);
                tryLock(lockName,uuid,expire);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
        //一旦加锁成功，开启定时任务重置过期时间
        reNewExpiration(lockName, uuid, expire);
        return true;
    }

    public void unLock(String lockName,String uuid){
        /**
         * 如果根据lock和uuid返回值为0，说明不是你的锁，则返回null
         * 如果根据lock和uuid获取到锁并-1，如果值等于0了，则释放锁，返回1
         * 如果-1后没到0，则返回0
         */
        String script = "if redis.call('hexists',KEYS[1],ARGV[1]) == 0 " +
                "then " +
                "return nil " +
                "elseif redis.call('hincrby',KEYS[1],ARGV[1],-1) == 0 " +
                "then " +
                "return redis.call('del',KEYS[1]) " +
                "else " +
                "return 0 " +
                "end";
        Long flag = redisTemplate.execute(new DefaultRedisScript<>(script, Long.class), Arrays.asList(lockName), uuid);
        if (flag == null){
            throw new IllegalMonitorStateException("释放的锁失败");
        }
    }

    private void reNewExpiration(String lockName,String uuid,Integer expire){
        /**
         * 如果是自己占用着锁，那就再延长一次过期时间
         */
        String script = "if redis.call('hexists',KEYS[1],ARGV[1]) == 1 " +
                "then" +
                " return redis.call('expire',KEYS[1],ARGV[2]) " +
                "else" +
                " return 0 " +
                "end";
        new Timer().schedule(new TimerTask() {
            @Override
            public void run() {
                Boolean flag = redisTemplate.execute(new DefaultRedisScript<>(script, Boolean.class),
                        Arrays.asList(lockName), uuid, expire.toString());
                if (flag){
                    reNewExpiration(lockName, uuid, expire);
                }
            }
        },expire * 1000 / 3 );
    }


}
